/******************************************************************/
/*                                                                */
/*  Winpooch : Windows Watchdog                                   */
/*  Copyright (C) 2004  Benoit Blanchon                           */
/*                                                                */
/*  This program is free software; you can redistribute it        */
/*  and/or modify it under the terms of the GNU General Public    */
/*  License as published by the Free Software Foundation; either  */
/*  version 2 of the License, or (at your option) any later       */
/*  version.                                                      */
/*                                                                */
/*  This program is distributed in the hope that it will be       */
/*  useful, but WITHOUT ANY WARRANTY; without even the implied    */
/*  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       */
/*  PURPOSE.  See the GNU General Public License for more         */
/*  details.                                                      */
/*                                                                */
/*  You should have received a copy of the GNU General Public     */
/*  License along with this program; if not, write to the Free    */
/*  Software Foundation, Inc.,                                    */
/*  675 Mass Ave, Cambridge, MA 02139, USA.                       */
/*                                                                */
/******************************************************************/


/******************************************************************/
/* Includes                                                       */
/******************************************************************/

// module's interface
#include "Alert.h"

// project's headers
#include "Assert.h"
#include "Language.h"
#include "Linked.h"
#include "Log.h"
#include "StringList.h"

// standard headers
#include <stdio.h>
#include <tchar.h>


/******************************************************************/
/* Macros                                                         */
/******************************************************************/

// helpers marco
#define arraysize(A) (sizeof(A)/sizeof(A[0]))


/******************************************************************/
/* Private data structures                                        */
/******************************************************************/


/******************************************************************/
/* Private variables                                              */
/******************************************************************/

// history linked list
static LINKED_LIST llHistory ;

static HWND hwndMain ;

static HSTRINGLIST hAlertStrings ;
static HSTRINGLIST hAdviceStrings ;


/******************************************************************/
/* Internal functions                                             */
/******************************************************************/

BOOL _Alert_PushHistory (ALERT *) ;
ALERT * _Alert_PopHistory () ;
void _Alert_Free (ALERT *) ;
void _Alert_ExpandString (LPTSTR szString, DWORD dwMaxLength, 
			  LPCTSTR szFormat, ALERT * p) ;
BOOL _Alert_IsAlreadyInHistory (ALERT *) ;
int _Alert_SubstractTime (ALERT * a, ALERT * b) ;


/******************************************************************/
/* Exported function : Init                                       */
/******************************************************************/

BOOL Alert_Init (HWND hwnd) 
{
  TCHAR szFile[MAX_PATH] ;

  Linked_InitList (&llHistory) ;

  _stprintf (szFile, TEXT("languages\\%s"), STR(_ALERTS_FILE_NAME)) ;
  hAlertStrings = StringList_Load (szFile) ;
  if( ! hAlertStrings ) return FALSE ;

  _stprintf (szFile, TEXT("languages\\%s"), STR(_ADVICES_FILE_NAME)) ;
  hAdviceStrings = StringList_Load (szFile) ;
  if( ! hAdviceStrings ) {
    StringList_Free (hAlertStrings) ;
    return FALSE ;
  }

  hwndMain = hwnd ;

  return TRUE ;
}


/******************************************************************/
/* Exported function : Uninit                                     */
/******************************************************************/

void Alert_Uninit () 
{
  while( llHistory.length )
    _Alert_Free (_Alert_PopHistory()) ;

  StringList_Free (hAdviceStrings) ;
  StringList_Free (hAlertStrings) ;

  Linked_UninitList (&llHistory) ;
}


/******************************************************************/
/* Exported function : ReloadStrings                              */
/******************************************************************/

BOOL Alert_ReloadStrings () 
{
  TCHAR		szFile[MAX_PATH] ;
  HSTRINGLIST	hNewAlertStrings ;
  HSTRINGLIST	hNewAdviceStrings ;

  _stprintf (szFile, TEXT("languages\\%s"), STR(_ALERTS_FILE_NAME)) ;
  hNewAlertStrings = StringList_Load (szFile) ;
  if( ! hNewAlertStrings ) return FALSE ;

  _stprintf (szFile, TEXT("languages\\%s"), STR(_ADVICES_FILE_NAME)) ;
  hNewAdviceStrings = StringList_Load (szFile) ;
  if( ! hNewAdviceStrings ) {
    StringList_Free (hNewAlertStrings) ;
    return FALSE ;
  }

  StringList_Free (hAlertStrings) ;
  hAlertStrings = hNewAlertStrings ;
 
  StringList_Free (hAdviceStrings) ;
  hAdviceStrings = hNewAdviceStrings ;

  return TRUE ;
}


/******************************************************************/
/* Exported function : Send                                       */
/******************************************************************/

void Alert_Send (ALERTID id, ADVICEID advice, LPCTSTR arg0, LPCTSTR arg1, LPCTSTR arg2) 
{
  ALERT * pAlert ;
  TCHAR szLogMsg[256] ;

  pAlert = (ALERT*) calloc (1, sizeof(ALERT)) ;

  pAlert->id = id ;
  pAlert->advice = advice ;
  GetLocalTime (&pAlert->time) ;
  pAlert->args[0] = _tcsdup (arg0) ;
  pAlert->args[1] = _tcsdup (arg1) ;
  pAlert->args[2] = _tcsdup (arg2) ;

  if( ! _Alert_IsAlreadyInHistory (pAlert) )
    {
      _Alert_PushHistory (pAlert) ;
      
      Alert_GetString (szLogMsg, arraysize(szLogMsg), pAlert) ;
      Log_Print (szLogMsg) ;
      
      SendMessage (hwndMain, WM_ALERT, 0, (LPARAM)pAlert) ;
    }
  else _Alert_Free (pAlert) ;
}


/******************************************************************/
/* Exported function : Alert_SendFormated                         */
/******************************************************************/

void Alert_SendFormated (ALERTID id, ADVICEID advice, LPCTSTR szFormat, ...) 
{
  TCHAR szMessage[256] ;
  va_list va ;
  
  va_start (va, szFormat) ;
  _vsntprintf (szMessage, 256, szFormat, va) ;
  va_end (va) ;

  Alert_Send (id, advice, szMessage, NULL, NULL) ;
}


/******************************************************************/
/* Internal function : _Alert_PushHistory                         */
/******************************************************************/

BOOL _Alert_PushHistory (ALERT * pAlert) 
{
  while( llHistory.length > ALERT_HISTORY_SIZE )
    _Alert_Free (_Alert_PopHistory()) ;

  return Linked_PushFront (&llHistory, pAlert) ;
}


/******************************************************************/
/* Internal function : _Alert_PopHistory                          */
/******************************************************************/

ALERT * _Alert_PopHistory ()
{
  return Linked_PopBack (&llHistory) ;
}


/******************************************************************/
/* Internal function : _Alert_Free                                */
/******************************************************************/

void _Alert_Free (ALERT * p) 
{
  free ((void*)p->args[0]) ;
  free ((void*)p->args[1]) ;
  free (p) ;
}


/******************************************************************/
/* Internal function : _Alert_IsAlreadyInHistory                  */
/******************************************************************/

BOOL _Alert_IsAlreadyInHistory (ALERT * a) 
{
  LINKED_NODE * p ;

  for( p=llHistory.front ; p ; p=p->next )
    {
      ALERT * b = (ALERT*)p->data ;
      int i ;
      int iTimeDiff ;

      iTimeDiff = _Alert_SubstractTime (a, b) ;

      // compare times
      if( abs(iTimeDiff) > ALERT_NO_REPEAT_TIME ) continue ;

      // compare strings
      for( i=0 ; i<ALERT_MAX_ARGS ; i++ )
	{
	  if( a->args[i]==NULL && b->args[i]!=NULL )
	    break ;
	  if( a->args[i]!=NULL && b->args[i]==NULL )
	    break ;
	  if( a->args[i]!=NULL && b->args[i]!=NULL )
	    if( 0!=_tcscmp(a->args[i],b->args[i]) )
	      break ;
	}
      
      // strings not equal ?
      if( i<ALERT_MAX_ARGS ) continue ;

      // same alert id ?
      if( a->id == b->id  ) return TRUE ;	

      // file modified after creation ?
      if( a->id==ALERT_DIR_FILEMODIFIED && b->id==ALERT_DIR_FILEADDED ) return TRUE ;
    }

  return FALSE ;
}


/******************************************************************/
/* Internal function : _Alert_IsAlreadyInHistory                  */
/******************************************************************/

int _Alert_SubstractTime (ALERT * a, ALERT * b) 
{
  LARGE_INTEGER lia, lib ;

  SystemTimeToFileTime (&a->time, (FILETIME*)&lia) ;
  SystemTimeToFileTime (&b->time, (FILETIME*)&lib) ;
  
  return (lia.QuadPart - lib.QuadPart) / 10000 ;
}



/******************************************************************/
/* Exported function : Alert_GetString                            */
/******************************************************************/

void Alert_GetString (LPTSTR szString, DWORD dwMaxLength, ALERT * p) 
{
  LPCTSTR szFormat = NULL ;

  szFormat = StringList_Get (hAlertStrings, p->id) ;

  if( ! szFormat )
    // TODO : move string in another module
    _sntprintf (szString, dwMaxLength, TEXT("Invalid alert id 0x%.08X"), p->id) ;
  else 
    _Alert_ExpandString (szString, dwMaxLength, szFormat, p) ;
}


/******************************************************************/
/* Exported function : Alert_GetString                            */
/******************************************************************/

void Alert_GetAdvice (LPTSTR szString, DWORD dwMaxLength, ALERT * p) 
{
  LPCTSTR szFormat = NULL ;

  szFormat = StringList_Get (hAdviceStrings, p->advice) ;

  if( ! szFormat )
    // TODO : move string in another module
    _sntprintf (szString, dwMaxLength, TEXT("Invalid advice id 0x%.08X"), p->advice) ;
  else 
    _Alert_ExpandString (szString, dwMaxLength, szFormat, p) ;
}


/******************************************************************/
/* Internal function : _Alert_ExpandString                        */
/******************************************************************/

void _Alert_ExpandString (LPTSTR szString, DWORD dwMaxLength, 
			  LPCTSTR szFormat, ALERT * p) 
{
  int iSrc, iDst ;

  dwMaxLength-- ;

  iSrc = 0 ;
  iDst = 0 ;

  while( iDst<dwMaxLength && szFormat[iSrc] ) 
    {
      if( szFormat[iSrc]==TEXT('$') )
	{
	  int iArg = szFormat[++iSrc] - TEXT('1') ;
	  
	  if( iArg>=0 && iArg<ALERT_MAX_ARGS && p->args[iArg] )
	    {
	      int i ;
	      for( i=0 ; p->args[iArg][i] ; i++ )
		if( iDst<dwMaxLength )
		  szString[iDst++] = p->args[iArg][i] ;
	    }
	  else if( szFormat[iSrc]==TEXT('$') )
	    {
	      szString[iDst] = TEXT('$') ;
	    }
	}
      else szString[iDst++] = szFormat[iSrc] ;
      
      iSrc++ ;
    }

  szString[iDst] = 0 ;
}


/******************************************************************/
/* Exported function : Alert_GetHistory                           */
/******************************************************************/

LINKED_LIST * Alert_GetHistory () 
{
  return &llHistory ;
}
